<!DOCTYPE html>
<html lang='zh-CN'>
  <head>
    <meta charset='UTF-8'>
    <meta name='viewport" content="width=device-width,initial-scale=1'>
    <title>upload</title>
  </head>
  <body>
    <input type='file' onchange='uploadFile()' id='files'>
    <button onclick='abort()'>abort</button>
  </body>
  <script>
    /**
     * https://github.com/rxaviers/async-pool/tree/v1.3.0
     */
    async function asyncPool(poolLimit, iterable, iteratorFn) {
      const ret = [];
      const executing = new Set();
      for (const item of iterable) {
        const p = Promise.resolve().then(() => iteratorFn(item, iterable));
        ret.push(p);
        executing.add(p);
        const clean = () => executing.delete(p);
        p.then(clean).catch(clean);
        if (executing.size >= poolLimit) {
          await Promise.race(executing);
        }
      }
      return Promise.all(ret);
    }

    let xhr;

    const sendFileData = param => new Promise((resolve, reject) => {
      let oldTime;
      let oldLoaded;
      xhr = new XMLHttpRequest();
      xhr.open('PUT', param.url, true);
      xhr.upload.onloadstart = function () {
        oldTime = new Date().getTime();
        oldLoaded = 0;
        console.info('开始上传分片：', param.index);
      };
      xhr.upload.onprogress = function (event) {
        let nowTime = new Date().getTime();
        let intervalTime = (nowTime - oldTime) / 1000;
        oldTime = nowTime;

        let nowLoaded = event.loaded;
        let intervalLoad = nowLoaded - oldLoaded;
        oldLoaded = nowLoaded;

        // 上传速度
        let speed = intervalLoad / intervalTime;

        console.info(
          '分片：', param.index,
          '进度：', Math.round(event.loaded / event.total * 100) + '%',
          '速度：', (speed / 1024 / 1024).toFixed(1) + 'M/s',
          '剩余时间：', ((event.total - event.loaded) / speed).toFixed(1) + 's'
        );
      };
      xhr.onload = function () {
        console.info('上传第 ' + param.index + ' 个分片完成：' + param.url)
        resolve(param.url);
      };
      xhr.onabort = function (event) {
        console.warn('上传已取消');
        console.warn(event);
        reject('abort');
      }
      xhr.send(param.file);
    });

    function abort() {
      xhr.abort();
    }

    // 分片大小 5M
    const partSize = 50 * 1024 * 1024;

    function uploadFile() {
      const file = document.getElementById('files').files[0];
      const partTotal = Math.ceil(file.size / partSize);
      console.info('文件大小：', (file.size / 1024 / 1024) + 'Mb', '分片数：', partTotal);

      fetch('http://127.0.0.1:8080/multipart/create?fileName=' + file.name + '&partTotal=' + partTotal)
        .then(response => response.json())
        .then(json => {
          let param = [];

          for (let i = 0; i < json.partUrls.length; i++) {
            param.push({
              index: i,
              url: json.partUrls[i],
              file: file.slice(i * partSize, Math.min(file.size, (i + 1) * partSize))
            });
          }

          asyncPool(10, param, sendFileData)
            .then(res => console.info(res))
            .then(() => fetch('http://127.0.0.1:8080/multipart/complete?fileName=' + file.name + '&uploadId=' + json.uploadId)
              .then(response => response.json())
              .then(json => console.info(json))
            )
        }).catch(err => console.error('Request Failed', err));
    }
  </script>
</html>